﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Reflection;

namespace IMDBApi.Contracts
{
    /// <summary>
    /// The base class to interact with the IMDb iPhone API.
    /// </summary>
    public class IMDBBase
    {
        /// <summary>
        /// Hardcoded Api Version.
        /// </summary>
        private const string _api = "v1";

        /// <summary>
        /// Hardcoded Application Identification.
        /// </summary>
        private const string _appId = "iphone1_1";

        /// <summary>
        /// Hardcoded Private Api Key which is needed to sign the requests.
        /// </summary>
        private const string _apiKey = "2wex6aeu6a8q9e49k7sfvufd6rhh0n";

        /// <summary>
        /// Hardcoded API Host.
        /// </summary>
        private const string _host = "app.imdb.com";

        /// <summary>
        /// Hardcoded API Policy.
        /// </summary>
        private const string _apiPolicy = "app1_1";

        /// <summary>
        /// Locale.
        /// Default is en_US.
        /// Possible Locales are: en_US, de_DE, fr_FR, pt_PT, it_IT.
        /// </summary>
        private string _locale = "en_US";

        #region Protected

        /// <summary>
        /// Makes the Request
        /// 
        /// This internal method is called by every function which makes an request to the IMDb iPhone API.
        /// It also checks if the request was successfull by text decoding the answer. The json_decode is required
        /// in order to test.
        /// </summary>
        /// <param name="function">The function which the request is regarded to.</param>
        /// <param name="arguments">The additional arguments for the functions.</param>
        /// <returns>Returns the content as a JSON formatted string.</returns>
        protected string MakeRequest(string function, Dictionary<string, string> arguments = null)
        {
            if (function == string.Empty)
            {
                throw new Exception(MethodBase.GetCurrentMethod().Name + ": There is no function or argument present.");
            }
            
            Dictionary<string, string> parameter = CreateParameter(arguments);
            string baseUrl = CreateBaseUrl(function, parameter);
            string signedUrl = CreateSignedUrl(baseUrl);
            string json = Utils.file_get_contents(signedUrl);

            if (json == string.Empty)
            {
                throw new Exception(MethodBase.GetCurrentMethod().Name + ": There is a problem with file_get_contents().");
            }
                
            return json;
        }

        /// <summary>
        /// Creates the parameter for the base url.
        /// 
        /// This internal method is called by makeRequest() in order to create all parameter and sort them.
        /// </summary>
        /// <param name="arguments">(Optional) The additional arguments for the functions.</param>
        /// <returns>Returns the parameter as a key-sorted array.</returns>
        protected Dictionary<string, string> CreateParameter(Dictionary<string, string> arguments = null)
        {
 
            var parameter = new Dictionary<string, string>
            {
                    { "api", _api },
                    { "appid", _appId },
                    { "device", Utils.ComputeSha1(Utils.GetCurrentUnixTimestampMillis().ToString(CultureInfo.InvariantCulture)) },
                    { "locale", _locale },
                    { "timestamp", DateTime.Now.ToString(CultureInfo.InvariantCulture) }
                };

            if (arguments != null)
            {
                foreach (var key in arguments.Keys.Where(key => !parameter.ContainsKey(key)))
                {
                    parameter.Add(key, arguments[key]);
                }
            }

            return parameter;
        }

        /// <summary>
        /// Creates the base url with everything but the signature.
        /// 
        /// This internal method is called by makeRequest() in order to make the base url.
        /// </summary>
        /// <param name="function">The function which the request is regarded to.</param>
        /// <param name="parameter">The parameter for the url.</param>
        /// <returns>Returns the base url ready to sign.</returns>
        protected string CreateBaseUrl(string function, Dictionary<string, string> parameter)
        {
            if (function == string.Empty || parameter == null)
            {
                throw new Exception(MethodBase.GetCurrentMethod().Name + ": Function and parameter are required.");
            }
            
            string baseUrl = "http://" + _host
                                            + function
                                                + "?";

            foreach (var key in parameter.Keys)
            {
                baseUrl += key + "=" + parameter[key] + "&";
            }

            baseUrl += "sig=" + _apiPolicy;
            return baseUrl;
        }

        /// <summary>
        /// Creates the signed url.
        /// 
        /// It makes the sha1 hash of the base url and appends it to the base url.
        /// This internal method is called by makeRequest() in order to sign the base url with the sha1.
        /// </summary>
        /// <param name="baseUrl">The base url which is to be signed.</param>
        /// <returns>Returns the complete signed url.</returns>
        protected string CreateSignedUrl(string baseUrl)
        {
            if (baseUrl == string.Empty)
            {
                throw new Exception(MethodBase.GetCurrentMethod().Name + ": The base url is required.");
            }

            return baseUrl + "-" + Utils.ComputeSha256(_apiKey, baseUrl);
        }

        #endregion
    }
}